<template>
    <div class="weather-page">
        <div class="header">
            <div class="title">天气预报 · 生产活动预警</div>
            <div class="endpoint" title="分析接口">{{ base }}</div>
        </div>

        <div class="actions">
            <label class="region-label">地区</label>
            <select class="select" v-model="region">
                <option v-for="r in regions" :key="r" :value="r">{{ r }}</option>
            </select>
            <input class="input" v-model.trim="customRegion" placeholder="自定义地区（可选）" />
            <label class="region-label">自动刷新</label>
            <input type="checkbox" v-model="autoRefresh" @change="toggleAuto()" />
            <label class="region-label">间隔(秒)</label>
            <input class="input small" type="number" min="10" v-model.number="intervalSec" @change="restartAuto()" />
            <label class="region-label">实时推送</label>
            <input type="checkbox" v-model="useSse" @change="toggleSse()" />
            <button class="btn" :disabled="loading" @click="load">刷新</button>
        </div>

        <div v-if="nowcast" class="nowcast">
            <span>实时：</span>
            <span>{{ displayRegion }} · </span>
            <span>{{ nowcast.temperatureC }} °C · </span>
            <span>{{ translateSummary(nowcast.summary) }}</span>
            <span v-if="typeof nowcast.rainHours === 'number' && (nowcast.rainHours || 0) > 0"> · 降雨将持续至少 {{
                nowcast.rainHours }} 小时</span>
            <span v-else> · 未来短时无显著降水</span>
        </div>

        <div v-if="error" class="error">{{ error }}</div>

        <div class="cards" v-if="items.length">
            <div v-for="(it, i) in items" :key="i" class="card">
                <div class="row top">
                    <div class="date">{{ it.date }}</div>
                    <span class="badge" :class="levelClass(it.level)">{{ it.level || 'none' }}</span>
                </div>
                <div class="row area">地区：{{ displayRegion }}</div>
                <div class="row temp">{{ it.temperatureC }} °C</div>
                <div class="row sum">{{ translateSummary(it.summary) }}</div>
                <div class="row reason" v-if="it.level && it.level !== 'none'">{{ it.reason }}</div>
            </div>
        </div>

        <div v-else-if="!loading" class="empty">暂无数据</div>
        <div class="footer">上次更新：{{ lastUpdated || '—' }}</div>
    </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import * as signalR from '@microsoft/signalr'
import { read, write } from '@/assets/Axios/axiosHttple'

type Forecast = { date: string; temperatureC: number; summary?: string | null }
type Warn = { date?: string; level?: string; reason?: string }
type Row = Forecast & Warn

// 统一通过 axiosHttple 调用后端分析接口
const base = 'contractmanagementread'
const loading = ref(false)
const error = ref('')
const items = ref<Row[]>([])
const nowcast = ref<{ temperatureC: number; summary: string; rainHours?: number | null } | null>(null)
const regions = ['全国', '北京', '上海', '广东-广州', '浙江-杭州', '江苏-南京', '四川-成都', '重庆', '湖北-武汉', '陕西-西安']
const region = ref<string>(regions[0])
const customRegion = ref('')
const displayRegion = computed(() => customRegion.value.trim() || region.value)
const autoRefresh = ref(false)
const intervalSec = ref(60)
const lastUpdated = ref('')
let timer: any = null
let hub: signalR.HubConnection | null = null
const useSse = ref(false)

function levelClass(level?: string) {
    switch ((level || '').toLowerCase()) {
        case 'red': return 'red'
        case 'orange': return 'orange'
        default: return 'none'
    }
}

function translateSummary(s?: string | null): string {
    const en = (s || '').toLowerCase()
    const map: Record<string, string> = {
        'clear': '晴',
        'mainly clear': '多晴',
        'partly cloudy': '多云',
        'overcast': '阴',
        'cool': '凉爽',
        'chilly': '寒冷',
        'balmy': '温和',
        'bracing': '清冷',
        'fog': '雾',
        'depositing rime fog': '霜雾',
        'light drizzle': '小毛毛雨',
        'moderate drizzle': '毛毛雨',
        'dense drizzle': '大毛毛雨',
        'light freezing drizzle': '小冻雨',
        'dense freezing drizzle': '大冻雨',
        'slight rain': '小雨',
        'moderate rain': '中雨',
        'heavy rain': '大雨',
        'light freezing rain': '小冻雨',
        'heavy freezing rain': '大冻雨',
        'slight snow fall': '小雪',
        'moderate snow fall': '中雪',
        'heavy snow fall': '大雪',
        'snow grains': '米雪',
        'slight rain showers': '小阵雨',
        'moderate rain showers': '中阵雨',
        'violent rain showers': '暴阵雨',
        'slight snow showers': '小阵雪',
        'heavy snow showers': '大阵雪',
        'thunderstorm': '雷暴',
        'thunderstorm with slight hail': '雷暴伴有小冰雹',
        'thunderstorm with heavy hail': '雷暴伴有大冰雹'
    }
    return map[en] || (s || '—')
}

// WMO 天气代码 → 英文描述（用于实时接口返回的 weathercode）
function mapWeatherCode(code: number): string {
    const m: Record<number, string> = {
        0: 'Clear',
        1: 'Mainly clear', 2: 'Partly cloudy', 3: 'Overcast',
        45: 'Fog', 48: 'Depositing rime fog',
        51: 'Light drizzle', 53: 'Moderate drizzle', 55: 'Dense drizzle',
        56: 'Light freezing drizzle', 57: 'Dense freezing drizzle',
        61: 'Slight rain', 63: 'Moderate rain', 65: 'Heavy rain',
        66: 'Light freezing rain', 67: 'Heavy freezing rain',
        71: 'Slight snow fall', 73: 'Moderate snow fall', 75: 'Heavy snow fall',
        77: 'Snow grains',
        80: 'Slight rain showers', 81: 'Moderate rain showers', 82: 'Violent rain showers',
        85: 'Slight snow showers', 86: 'Heavy snow showers',
        95: 'Thunderstorm', 96: 'Thunderstorm with slight hail', 99: 'Thunderstorm with heavy hail'
    }
    return m[code] ?? 'Unknown'
}

// 方案A：前端直连 Open‑Meteo 获取实时天气
async function geocode(name: string) {
    const raw = (name || '').trim()
    // 特例："全国"/"中国" 统一按北京坐标处理，避免地理编码偏差
    const normalized = raw.replace(/\s+/g, '')
    if (['全国', '中国', 'china', 'cn', '中华人民共和国'].includes(normalized.toLowerCase())) {
        return { lat: 39.9042, lon: 116.4074, tz: 'Asia/Shanghai' }
    }
    // 特例：北京（避免解析到同名国外地点）
    if (/北京|beijing/i.test(normalized)) {
        return { lat: 39.9042, lon: 116.4074, tz: 'Asia/Shanghai' }
    }
    // 支持 "lat,lon" 直接输入
    const m = raw.match(/^\s*(-?\d+(?:\.\d+)?)\s*[,，]\s*(-?\d+(?:\.\d+)?)\s*$/)
    if (m) return { lat: Number(m[1]), lon: Number(m[2]), tz: 'auto' }
    const parts = raw.split(/[-—·\s]+/).filter(Boolean)
    const candidates = [raw, parts[parts.length - 1] || raw]
    for (const q of candidates) {
        const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(q)}&count=5&language=zh&format=json`
        const r = await fetch(url)
        if (!r.ok) continue
        const j = await r.json()
        const results = Array.isArray(j?.results) ? j.results : []
        if (results.length) {
            // 先按国家代码=CN，再按 admin1/市名包含“北京”过滤
            const cn = results.filter((x: any) => (x.country_code || '').toLowerCase() === 'cn')
            const preferBj = cn.find((x: any) => /北京/i.test(`${x.admin1 || ''}${x.name || ''}`))
            const pick = preferBj || cn[0] || results[0]
            return { lat: pick.latitude as number, lon: pick.longitude as number, tz: (pick.timezone as string) || 'auto' }
        }
    }
    throw new Error('未找到该地区')
}

async function fetchForecast(lat: number, lon: number, tz: string) {
    const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=${encodeURIComponent(tz)}`
    const r = await fetch(url)
    if (!r.ok) throw new Error('天气接口失败')
    const j = await r.json()
    const dates: string[] = j?.daily?.time || []
    const tmax: number[] = j?.daily?.temperature_2m_max || []
    const tmin: number[] = j?.daily?.temperature_2m_min || []
    const codes: number[] = j?.daily?.weathercode || []
    const out: Forecast[] = []
    for (let i = 0; i < Math.min(5, dates.length); i++) {
        const avg = Math.round(((tmax[i] ?? 0) + (tmin[i] ?? 0)) / 2)
        out.push({ date: dates[i], temperatureC: avg, summary: mapWeatherCode(codes[i] ?? 0) })
    }
    return out
}

// 获取当前实况 + 降雨持续时间（小时级预估）
async function fetchCurrent(lat: number, lon: number, tz: string) {
    // 使用 current_weather + hourly 降水，基于 current_weather.time 精确计算“持续小时”
    const u1 = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true&hourly=precipitation&temperature_unit=celsius&timezone=${encodeURIComponent(tz)}`
    const r1 = await fetch(u1)
    if (!r1.ok) throw new Error('当前天气接口失败')
    const j1 = await r1.json()
    const cur = j1?.current_weather || {}
    const t = Math.round(Number(cur.temperature ?? 0))
    const code = Number((cur.weathercode ?? cur.weather_code) ?? 0)
    const summary = mapWeatherCode(code)

    // 每小时降水(未来) 用于估算“将持续至少X小时”
    let rainHours: number | null = null
    try {
        const times: string[] = j1?.hourly?.time || []
        const prec: number[] = j1?.hourly?.precipitation || []
        const curIso: string = cur?.time || ''
        // 找到当前时刻在 hourly 中的索引（精确到小时）
        let idx = times.findIndex((s: string) => s === curIso)
        if (idx < 0 && curIso) {
            const hour = curIso.slice(0, 13) // yyyy-mm-ddThh
            idx = times.findIndex((s: string) => s.slice(0, 13) === hour)
        }
        if (idx < 0) idx = 0
        let hours = 0
        for (let i = idx; i < prec.length; i++) {
            if (prec[i] && prec[i] > 0) hours++
            else break
        }
        rainHours = hours
    } catch { rainHours = null }

    return { temperatureC: t, summary, rainHours }
}

async function load() {
    loading.value = true
    error.value = ''
    nowcast.value = null
    try {
        // 1) 实时天气：地理编码 → 预报（Open‑Meteo）
        // “全国”不再强制北京，直接按用户选择/自定义解析
        const name = displayRegion.value
        const { lat, lon, tz } = await geocode(name)
        const list: Forecast[] = await fetchForecast(lat, lon, tz)
        // 实时实况
        let now: { temperatureC: number; summary: string; rainHours?: number | null } | null = null
        try {
            now = await fetchCurrent(lat, lon, tz)
        } catch { now = null }

        // 2) 分析预警（带地区）
        const p2 = new URLSearchParams()
        if (displayRegion.value) p2.set('region', displayRegion.value)
        let warns: Warn[] = []
        try {
            const res = await write.post(`${base}/WeatherForecast/analyze`, list, { params: Object.fromEntries(p2.entries()) })
            if (res?.status === 200) warns = Array.isArray(res.data) ? res.data : (Array.isArray(res?.data?.data) ? res.data.data : [])
        } catch {
            warns = []
        }

        // 3) 合并
        const map = new Map<string, Warn>()
        warns.forEach(w => { if (w.date) map.set(w.date, w) })
        const daily = list.map(f => ({
            ...f,
            ...(map.get(f.date) || {})
        }))
        // 在最前插入“当前实况”卡片（不参与预警，仅展示实时）
        nowcast.value = now
        if (now) {
            items.value = [
                { date: '实时', temperatureC: now.temperatureC, summary: translateSummary(now.summary), level: 'none', reason: '' } as any,
                ...daily
            ]
        } else {
            items.value = daily
        }
        // 成功后清空错误提示（若 analyze 不可用，仅提示信息不报错中断）
        error.value = ''
        lastUpdated.value = new Date().toLocaleString()
    } catch (e: any) {
        error.value = e?.message || String(e)
    } finally {
        loading.value = false
    }
}

function clearAuto() { if (timer) { clearInterval(timer); timer = null } }
function startAuto() {
    clearAuto()
    if (autoRefresh.value && intervalSec.value >= 10) {
        timer = setInterval(() => { load() }, intervalSec.value * 1000)
    }
}
function toggleAuto() { startAuto() }
function restartAuto() { startAuto() }

async function toggleSse() {
    if (useSse.value) {
        if (!hub) {
            hub = new signalR.HubConnectionBuilder()
                .withUrl(`/weatherHub`)
                .withAutomaticReconnect()
                .configureLogging(signalR.LogLevel.Information)
                .build()
            hub.on('WeatherUpdated', (payload: any) => {
                try {
                    const list: Forecast[] = Array.isArray(payload) ? payload : []
                    // 收到服务端的最新数据后，按同样流程调用 analyze
                    const run = async () => {
                        const p2 = new URLSearchParams()
                        if (displayRegion.value) p2.set('region', displayRegion.value)
                        const res = await write.post(`${base}/WeatherForecast/analyze`, list, { params: Object.fromEntries(p2.entries()) })
                        const warns: Warn[] = Array.isArray(res?.data) ? res.data : (Array.isArray(res?.data?.data) ? res.data.data : [])
                        const map = new Map<string, Warn>()
                        warns.forEach(w => { if (w.date) map.set(w.date, w) })
                        items.value = list.map(f => ({ ...f, ...(map.get(f.date) || {}) }))
                        lastUpdated.value = new Date().toLocaleString()
                    }
                    run().catch(() => { })
                } catch { /* ignore */ }
            })
        }
        if (hub.state !== signalR.HubConnectionState.Connected) await hub.start()
    } else {
        if (hub && hub.state === signalR.HubConnectionState.Connected) await hub.stop()
    }
}

onMounted(() => { load(); startAuto() })
watch(region, () => { load() })
watch(customRegion, () => { if (customRegion.value.trim()) load() })
</script>

<style scoped>
.weather-page {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 12px;
}

.header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}

.title {
    font-weight: 600;
}

.endpoint {
    font-size: 12px;
    color: #888;
}

.actions {
    display: flex;
    gap: 8px;
}

.footer {
    color: #888;
    font-size: 12px;
    margin-top: 8px;
}

.nowcast {
    padding: 8px 10px;
    background: #f5f7fa;
    border: 1px solid #e4e7ed;
    border-radius: 6px;
    color: #333;
}

.input.small {
    width: 80px;
}

.btn {
    padding: 6px 12px;
    border: 1px solid #409eff;
    background: #409eff;
    color: #fff;
    border-radius: 6px;
    cursor: pointer;
}

.error {
    color: #c62828;
    background: #fdecea;
    border: 1px solid #f5c6cb;
    padding: 8px;
    border-radius: 6px;
}

.empty {
    color: #909399;
    padding: 12px;
}

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 12px;
}

.card {
    border: 1px solid #eee;
    border-radius: 8px;
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: #fff;
}

.row.top {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.date {
    font-weight: 600;
}

.temp {
    font-size: 20px;
    font-weight: 600;
}

.sum {
    color: #666;
}

.reason {
    color: #333;
}

.badge {
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 12px;
    border: 1px solid #ddd;
}

.badge.red {
    background: #fdecea;
    color: #c62828;
    border-color: #f5c6cb;
}

.badge.orange {
    background: #fff3e0;
    color: #e65100;
    border-color: #ffcc80;
}

.badge.none {
    background: #f5f7fa;
    color: #909399;
    border-color: #e4e7ed;
}
</style>
